package com.xzzz.irda.guardcat.server.metric.redis;

import cn.hutool.core.collection.CollUtil;
import com.xzzz.common.base.pojo.R;
import com.xzzz.common.base.util.SortUtil;
import com.xzzz.common.cache.pojo.RedisInfoEnum;
import com.xzzz.common.cache.pojo.RedisInfoRes;
import io.lettuce.core.RedisFuture;
import io.lettuce.core.api.async.RedisAsyncCommands;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * Redis 信息接口
 */
@Slf4j
@RestController
@RequestMapping("/guardcat/redis")
@ConditionalOnClass(value = RedisTemplate.class)
public class GuardcatRedisController {

    @Autowired(required = false)
    private StringRedisTemplate redisTemplate;

    /**
     * 获取 RedisInfo 信息(List格式)
     *
     * @return RedisInfo
     * @apiNote Redis 字段说明
     * ──────────────────── 基础信息 ────────────────────
     * redis_version: redis 版本
     * os: 宿主系统版本
     * arch_bits: 指令集 64/32 位
     * run_id: 随机标识符
     * tcp_port: 端口
     * uptime_in_seconds: 启动秒数
     * uptime_in_days: 启动天数
     * connected_clients: 客户端数
     * blocked_clients: 等待阻塞命令
     * <p>
     * ──────────────────── 集群信息 ────────────────────
     * redis_mode(standalone): redis模式
     * role: 服务器角色
     * connected_slaves: 从服务器数量
     * <p>
     * ──────────────────── 内存信息 ────────────────────
     * used_memory_human: redis 分配的内存总量
     * used_memory_peak_human: redis 内存消耗峰值
     * used_memory_peak_perc: 当前内存占峰值的百分比 即(used_memory/ used_memory_peak) *100%
     * used_memory_rss_human: 从操作系统的角度，返回 Redis 已分配的内存总量（俗称常驻集大小）。这个值和 top 、 ps等命令的输出一致。
     * used_memory_lua_human: lua 脚本占用的内存
     * total_system_memory_human: 系统总内存
     * <p>
     * ──────────────────── 备份信息 ────────────────────
     * rdb_changes_since_last_save: 距离最近一次数据持久化过去了多少秒
     * rdb_last_save_time: 最近创建rdb文件的时间
     * rdb_last_bgsave_time_sec: 最近创建rdb文件所消耗的时间
     * <p>
     * ──────────────────── 命令信息 ────────────────────
     * total_commands_processed: 服务器执行的命令数
     * instantaneous_ops_per_sec: 每秒执行的命令数
     * keyspace_hits: 命中成功的数量
     * keyspace_misses: 命中失败的数量
     * <p>
     * ──────────────────── 主键信息 ────────────────────
     * expired_keys: 过期自定删除的键数量
     * evicted_keys: 因为容量限制而驱逐的键数量
     */
    @GetMapping("/info/list")
    public R<List<RedisInfoRes>> infoList() {
        List<RedisInfoRes> infos = new ArrayList<>();
        infoMap().getData().forEach((k, v) -> {
            RedisInfoRes info = new RedisInfoRes();
            info.setKey(k);
            info.setValue(v);
            info.setDesc(RedisInfoEnum.getDesc(k));
            infos.add(info);
        });

        return R.ok(CollUtil.reverse(infos));
    }

    /**
     * 获取 RedisInfo 信息(Map格式)
     *
     * @return RedisInfo
     */
    @GetMapping("/info/map")
    public R<Map<String, String>> infoMap() {
        Map<String, String> map = new HashMap(redisTemplate.getRequiredConnectionFactory().getConnection().info());
        return R.ok(SortUtil.sortMap(map));
    }

    /**
     * 慢查询日志
     */
    @GetMapping("/slowlog")
    public R<List<SlowLog>> slowLog() {
        List<Object> slowlogs = redisTemplate.execute(new RedisCallback<List<Object>>() {
            @Override
            public List<Object> doInRedis(RedisConnection connection) throws DataAccessException {
                System.out.println(connection.getClass());
                RedisAsyncCommands redisAsyncCommands = (RedisAsyncCommands) connection.getNativeConnection();
                RedisFuture<List<Object>> future = redisAsyncCommands.slowlogGet(10);
                try {
                    return future.get();
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });

        List<SlowLog> result = slowlogs.stream()
                .map(e -> SlowLog.from(e))
                .collect(Collectors.toList());
        return R.ok(result);
    }
}
